package com.datagateway.performance;

import com.datagateway.component.*;
import com.datagateway.model.ProcessedData;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 性能基准测试
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@ExtendWith(MockitoExtension.class)
class PerformanceBenchmark {

    private DataBuffer dataBuffer;
    private GroovyScriptEngine scriptEngine;
    private DataValidationEngine validationEngine;
    private DataFormatConverter formatConverter;

    @Mock
    private AlertManager alertManager;

    @BeforeEach
    void setUp() {
        dataBuffer = new DataBuffer();
        dataBuffer.setAlertManager(alertManager);
        dataBuffer.initialize();

        scriptEngine = new GroovyScriptEngine();
        scriptEngine.setAlertManager(alertManager);
        scriptEngine.initialize();

        validationEngine = new DataValidationEngine();
        validationEngine.setAlertManager(alertManager);
        validationEngine.initialize();

        formatConverter = new DataFormatConverter();
        formatConverter.setAlertManager(alertManager);
        formatConverter.initialize();
    }

    @Test
    void benchmarkDataBufferThroughput() throws InterruptedException {
        int[] threadCounts = {1, 5, 10, 20, 50};
        int operationsPerThread = 1000;

        System.out.println("数据缓冲器吞吐量基准测试:");
        System.out.println("线程数\t吞吐量(ops/sec)\t平均延迟(ms)");

        for (int threadCount : threadCounts) {
            ExecutorService executor = Executors.newFixedThreadPool(threadCount);
            CountDownLatch latch = new CountDownLatch(threadCount);

            AtomicLong successCount = new AtomicLong(0);
            long startTime = System.currentTimeMillis();

            // 并发操作
            for (int i = 0; i < threadCount; i++) {
                executor.submit(() -> {
                    try {
                        for (int j = 0; j < operationsPerThread; j++) {
                            ProcessedData data = new ProcessedData();
                            data.setId("benchmark-" + j);
                            data.setContent("content-" + j);
                            data.setTimestamp(System.currentTimeMillis());

                            if (dataBuffer.addData(data)) {
                                successCount.incrementAndGet();
                            }
                        }
                    } finally {
                        latch.countDown();
                    }
                });
            }

            // 等待完成
            assertTrue(latch.await(60, TimeUnit.SECONDS));
            long endTime = System.currentTimeMillis();

            // 计算指标
            long totalOperations = threadCount * operationsPerThread;
            long processingTime = endTime - startTime;
            double throughput = (double) totalOperations / (processingTime / 1000.0);
            double avgLatency = (double) processingTime / totalOperations;

            System.out.println(threadCount + "\t" + String.format("%.2f", throughput) + "\t\t" + String.format("%.2f", avgLatency));

            executor.shutdown();
            dataBuffer.reset();
        }
    }

    @Test
    void benchmarkGroovyScriptPerformance() throws InterruptedException {
        int[] scriptComplexities = {1, 5, 10, 20};
        int operationsPerComplexity = 500;

        System.out.println("Groovy脚本性能基准测试:");
        System.out.println("脚本复杂度\t吞吐量(ops/sec)\t平均延迟(ms)");

        for (int complexity : scriptComplexities) {
            // 生成不同复杂度的脚本
            String script = generateComplexScript(complexity);

            long startTime = System.currentTimeMillis();
            int successCount = 0;

            // 执行脚本
            for (int i = 0; i < operationsPerComplexity; i++) {
                ProcessedData data = new ProcessedData();
                data.setId("benchmark-" + i);
                data.setContent("content-" + i);
                data.setTimestamp(System.currentTimeMillis());

                ProcessedData result = scriptEngine.executeScript(script, data);
                if (result != null) {
                    successCount++;
                }
            }

            long endTime = System.currentTimeMillis();

            // 计算指标
            long processingTime = endTime - startTime;
            double throughput = (double) operationsPerComplexity / (processingTime / 1000.0);
            double avgLatency = (double) processingTime / operationsPerComplexity;

            System.out.println(complexity + "\t\t" + String.format("%.2f", throughput) + "\t\t" + String.format("%.2f", avgLatency));

            scriptEngine.clearCache();
        }
    }

    @Test
    void benchmarkDataValidationPerformance() throws InterruptedException {
        String[] ruleSetNames = {"default", "user", "product", "order"};
        int operationsPerRuleSet = 1000;

        System.out.println("数据校验性能基准测试:");
        System.out.println("规则集\t\t吞吐量(ops/sec)\t平均延迟(ms)");

        for (String ruleSetName : ruleSetNames) {
            String testData = generateTestData(ruleSetName);

            long startTime = System.currentTimeMillis();
            int successCount = 0;

            // 执行校验
            for (int i = 0; i < operationsPerRuleSet; i++) {
                DataValidationEngine.ValidationResult result = validationEngine.validate(testData, ruleSetName);
                if (result.isValid()) {
                    successCount++;
                }
            }

            long endTime = System.currentTimeMillis();

            // 计算指标
            long processingTime = endTime - startTime;
            double throughput = (double) operationsPerRuleSet / (processingTime / 1000.0);
            double avgLatency = (double) processingTime / operationsPerRuleSet;

            System.out.println(ruleSetName + "\t\t" + String.format("%.2f", throughput) + "\t\t" + String.format("%.2f", avgLatency));
        }
    }

    @Test
    void benchmarkDataFormatConversionPerformance() throws InterruptedException {
        String[] formats = {"JSON", "XML", "CSV", "YAML"};
        int operationsPerFormat = 500;

        System.out.println("数据格式转换性能基准测试:");
        System.out.println("目标格式\t吞吐量(ops/sec)\t平均延迟(ms)");

        String jsonData = """
            {
                "name": "测试用户",
                "age": 25,
                "email": "test@example.com",
                "active": true,
                "metadata": {
                    "source": "kafka",
                    "timestamp": 1234567890
                }
            }
            """;

        for (String format : formats) {
            long startTime = System.currentTimeMillis();
            int successCount = 0;

            // 执行转换
            for (int i = 0; i < operationsPerFormat; i++) {
                DataFormatConverter.ConversionResult result = formatConverter.convert(jsonData, "JSON", format);
                if (result.isSuccess()) {
                    successCount++;
                }
            }

            long endTime = System.currentTimeMillis();

            // 计算指标
            long processingTime = endTime - startTime;
            double throughput = (double) operationsPerFormat / (processingTime / 1000.0);
            double avgLatency = (double) processingTime / operationsPerFormat;

            System.out.println(format + "\t\t" + String.format("%.2f", throughput) + "\t\t" + String.format("%.2f", avgLatency));
        }
    }

    @Test
    void benchmarkMemoryUsage() throws InterruptedException {
        int[] dataSizes = {100, 500, 1000, 5000, 10000};

        System.out.println("内存使用基准测试:");
        System.out.println("数据量\t内存使用(MB)\t平均每条数据(KB)");

        for (int dataSize : dataSizes) {
            long startMemory = getUsedMemory();
            long startTime = System.currentTimeMillis();

            // 添加数据
            for (int i = 0; i < dataSize; i++) {
                ProcessedData data = new ProcessedData();
                data.setId("memory-benchmark-" + i);
                data.setContent("content-" + i + " ".repeat(100)); // 增加内容大小
                data.setTimestamp(System.currentTimeMillis());
                dataBuffer.addData(data);
            }

            long endMemory = getUsedMemory();
            long endTime = System.currentTimeMillis();

            // 计算指标
            long memoryUsed = endMemory - startMemory;
            double memoryUsedMB = memoryUsed / (1024.0 * 1024.0);
            double avgMemoryPerData = memoryUsed / (dataSize * 1024.0);
            long processingTime = endTime - startTime;

            System.out.println(dataSize + "\t\t" + String.format("%.2f", memoryUsedMB) + "\t\t" + String.format("%.2f", avgMemoryPerData));

            // 清理内存
            dataBuffer.flushBuffer();
            System.gc();
            Thread.sleep(100);
        }
    }

    @Test
    void benchmarkConcurrentPerformance() throws InterruptedException {
        int[] concurrentLevels = {1, 5, 10, 20, 50, 100};
        int operationsPerLevel = 1000;

        System.out.println("并发性能基准测试:");
        System.out.println("并发数\t吞吐量(ops/sec)\t成功率(%)");

        for (int concurrentLevel : concurrentLevels) {
            ExecutorService executor = Executors.newFixedThreadPool(concurrentLevel);
            CountDownLatch latch = new CountDownLatch(concurrentLevel);

            AtomicLong successCount = new AtomicLong(0);
            AtomicLong failureCount = new AtomicLong(0);
            long startTime = System.currentTimeMillis();

            // 并发操作
            for (int i = 0; i < concurrentLevel; i++) {
                executor.submit(() -> {
                    try {
                        for (int j = 0; j < operationsPerLevel; j++) {
                            ProcessedData data = new ProcessedData();
                            data.setId("concurrent-" + j);
                            data.setContent("content-" + j);
                            data.setTimestamp(System.currentTimeMillis());

                            if (dataBuffer.addData(data)) {
                                successCount.incrementAndGet();
                            } else {
                                failureCount.incrementAndGet();
                            }
                        }
                    } finally {
                        latch.countDown();
                    }
                });
            }

            // 等待完成
            assertTrue(latch.await(60, TimeUnit.SECONDS));
            long endTime = System.currentTimeMillis();

            // 计算指标
            long totalOperations = concurrentLevel * operationsPerLevel;
            long processingTime = endTime - startTime;
            double throughput = (double) totalOperations / (processingTime / 1000.0);
            double successRate = (double) successCount.get() / totalOperations * 100;

            System.out.println(concurrentLevel + "\t\t" + String.format("%.2f", throughput) + "\t\t" + String.format("%.2f", successRate));

            executor.shutdown();
            dataBuffer.reset();
        }
    }

    @Test
    void benchmarkLatencyDistribution() throws InterruptedException {
        int operationCount = 1000;
        List<Long> latencies = new ArrayList<>();

        System.out.println("延迟分布基准测试:");
        System.out.println("操作类型\t平均延迟(ms)\tP50(ms)\tP90(ms)\tP95(ms)\tP99(ms)");

        // 测试数据缓冲延迟
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < operationCount; i++) {
            long opStart = System.nanoTime();
            
            ProcessedData data = new ProcessedData();
            data.setId("latency-test-" + i);
            data.setContent("content-" + i);
            data.setTimestamp(System.currentTimeMillis());
            dataBuffer.addData(data);
            
            long opEnd = System.nanoTime();
            latencies.add((opEnd - opStart) / 1_000_000); // 转换为毫秒
        }
        long endTime = System.currentTimeMillis();

        // 计算延迟统计
        latencies.sort(Long::compareTo);
        double avgLatency = latencies.stream().mapToLong(Long::longValue).average().orElse(0);
        long p50 = latencies.get((int) (operationCount * 0.5));
        long p90 = latencies.get((int) (operationCount * 0.9));
        long p95 = latencies.get((int) (operationCount * 0.95));
        long p99 = latencies.get((int) (operationCount * 0.99));

        System.out.println("数据缓冲\t" + String.format("%.2f", avgLatency) + "\t\t" + p50 + "\t" + p90 + "\t" + p95 + "\t" + p99);

        // 清理
        dataBuffer.flushBuffer();
        latencies.clear();
    }

    private String generateComplexScript(int complexity) {
        StringBuilder script = new StringBuilder();
        script.append("def transform(data) {\n");
        
        for (int i = 0; i < complexity; i++) {
            script.append("    data.metadata['field").append(i).append("'] = 'value").append(i).append("'\n");
            script.append("    data.metadata['processed").append(i).append("'] = true\n");
        }
        
        script.append("    data.content = data.content.toUpperCase()\n");
        script.append("    return data\n");
        script.append("}\n");
        
        return script.toString();
    }

    private String generateTestData(String ruleSetName) {
        switch (ruleSetName) {
            case "user":
                return """
                    {
                        "name": "测试用户",
                        "email": "test@example.com",
                        "phone": "13800138000",
                        "age": 25
                    }
                    """;
            case "product":
                return """
                    {
                        "name": "测试产品",
                        "price": 99.99,
                        "category": "电子产品",
                        "stock": 100
                    }
                    """;
            case "order":
                return """
                    {
                        "orderId": "ORD001",
                        "customerId": "CUST001",
                        "amount": 199.99,
                        "status": "PENDING"
                    }
                    """;
            default:
                return """
                    {
                        "id": "test-001",
                        "content": "test content",
                        "timestamp": 1234567890
                    }
                    """;
        }
    }

    private long getUsedMemory() {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
}
